<!DOCTYPE html>
<html lang="zh-Hans">
<head>

    <!--[if lt IE 9]>
        <style>body {display: none; background: none !important} </style>
        <meta http-equiv="Refresh" Content="0; url=//outdatedbrowser.com/" />
    <![endif]-->

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="format-detection" content="telephone=no" />
<meta name="author" content="孤星可" />



<meta name="description" content="原文: 编程的智慧


编程的智慧编程是一种创造性的工作，是一门艺术。精通任何一门艺术，都需要很多的练习和领悟，所以这里提出的“智慧”，并不是号称一天瘦十斤的减肥药，它并不能代替你自己的勤奋。然而由于软件行业喜欢标新立异，喜欢把简单的事情搞复杂，我希望这些文字能给迷惑中的人们指出一些正确的方向，让他们少走一些弯路，基本做到一分耕耘一分收获。">
<meta property="og:type" content="article">
<meta property="og:title" content="编程的智慧">
<meta property="og:url" content="http://blog.guxingke.com/2017/04/12/bian-cheng-de-zhi-hui/index.html">
<meta property="og:site_name" content="Guxingke's Blog">
<meta property="og:description" content="原文: 编程的智慧


编程的智慧编程是一种创造性的工作，是一门艺术。精通任何一门艺术，都需要很多的练习和领悟，所以这里提出的“智慧”，并不是号称一天瘦十斤的减肥药，它并不能代替你自己的勤奋。然而由于软件行业喜欢标新立异，喜欢把简单的事情搞复杂，我希望这些文字能给迷惑中的人们指出一些正确的方向，让他们少走一些弯路，基本做到一分耕耘一分收获。">
<meta property="og:updated_time" content="2017-04-18T06:23:33.000Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="编程的智慧">
<meta name="twitter:description" content="原文: 编程的智慧


编程的智慧编程是一种创造性的工作，是一门艺术。精通任何一门艺术，都需要很多的练习和领悟，所以这里提出的“智慧”，并不是号称一天瘦十斤的减肥药，它并不能代替你自己的勤奋。然而由于软件行业喜欢标新立异，喜欢把简单的事情搞复杂，我希望这些文字能给迷惑中的人们指出一些正确的方向，让他们少走一些弯路，基本做到一分耕耘一分收获。">

<link rel="apple-touch-icon" href= "/apple-touch-icon.png">


    <link rel="alternate" href="/atom.xml" title="Guxingke&#39;s Blog" type="application/atom+xml">



    <link rel="shortcut icon" href="/favicon.png">



    <link href="//cdn.bootcss.com/animate.css/3.5.1/animate.min.css" rel="stylesheet">



    <link href="//cdn.bootcss.com/fancybox/2.1.5/jquery.fancybox.min.css" rel="stylesheet">



    <script src="//cdn.bootcss.com/pace/1.0.2/pace.min.js"></script>
    <link href="//cdn.bootcss.com/pace/1.0.2/themes/blue/pace-theme-minimal.css" rel="stylesheet">


<link rel="stylesheet" href="/css/style.css">



<link href="//cdn.bootcss.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet">


<title>编程的智慧 | Guxingke&#39;s Blog</title>

<script src="//cdn.bootcss.com/jquery/2.2.4/jquery.min.js"></script>
<script src="//cdn.bootcss.com/clipboard.js/1.5.10/clipboard.min.js"></script>

<script>
    var yiliaConfig = {
        fancybox: true,
        animate: true,
        isHome: false,
        isPost: true,
        isArchive: false,
        isTag: false,
        isCategory: false,
        fancybox_js: "//cdn.bootcss.com/fancybox/2.1.5/jquery.fancybox.min.js",
        scrollreveal: "//cdn.bootcss.com/scrollReveal.js/3.1.4/scrollreveal.min.js",
        search: false
    }
</script>


    <script>
        yiliaConfig.jquery_ui = [true, "//cdn.bootcss.com/jqueryui/1.10.4/jquery-ui.min.js", "//cdn.bootcss.com/jqueryui/1.10.4/css/jquery-ui.min.css"];
    </script>



    <script> yiliaConfig.rootUrl = "\/";</script>






</head>
<body>
  <div id="container">
    <div class="left-col">
    <div class="overlay"></div>
<div class="intrude-less">
    <header id="header" class="inner">
        <a href="/" class="profilepic">
            <img src="/img/avatar.png" class="animated zoomIn">
        </a>
        <hgroup>
          <h1 class="header-author"><a href="/">孤星可</a></h1>
        </hgroup>

        
        <p class="header-subtitle">一个程序员</p>
        

        


        
            <div id="switch-btn" class="switch-btn">
                <div class="icon">
                    <div class="icon-ctn">
                        <div class="icon-wrap icon-house" data-idx="0">
                            <div class="birdhouse"></div>
                            <div class="birdhouse_holes"></div>
                        </div>
                        <div class="icon-wrap icon-ribbon hide" data-idx="1">
                            <div class="ribbon"></div>
                        </div>
                        
                        <div class="icon-wrap icon-link hide" data-idx="2">
                            <div class="loopback_l"></div>
                            <div class="loopback_r"></div>
                        </div>
                        
                        
                        <div class="icon-wrap icon-me hide" data-idx="3">
                            <div class="user"></div>
                            <div class="shoulder"></div>
                        </div>
                        
                    </div>
                    
                </div>
                <div class="tips-box hide">
                    <div class="tips-arrow"></div>
                    <ul class="tips-inner">
                        <li>菜单</li>
                        <li>标签</li>
                        
                        <li>友情链接</li>
                        
                        
                        <li>关于我</li>
                        
                    </ul>
                </div>
            </div>
        

        <div id="switch-area" class="switch-area">
            <div class="switch-wrap">
                <section class="switch-part switch-part1">
                    <nav class="header-menu">
                        <ul>
                        
                            <li><a href="/">主页</a></li>
                        
                            <li><a href="/archives/">所有文章</a></li>
                        
                            <li><a href="/moment/">时刻</a></li>
                        
                            <li><a href="/tags/">标签云</a></li>
                        
                            <li><a href="/about/">关于我</a></li>
                        
                        </ul>
                    </nav>
                    <nav class="header-nav">
                        <ul class="social">
                            
                                <a class="fa Email" href="mailto:admin@guxingke.com" title="Email"></a>
                            
                                <a class="fa GitHub" href="https://github.com/guxingke" title="GitHub"></a>
                            
                                <a class="fa RSS" href="/atom.xml" title="RSS"></a>
                            
                        </ul>
                    </nav>
                </section>
                
                
                <section class="switch-part switch-part2">
                    <div class="widget tagcloud" id="js-tagcloud">
                        <ul class="tag-list"><li class="tag-list-item"><a class="tag-list-link" href="/tags/Alfred/">Alfred</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Paw/">Paw</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/backup/">backup</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/caddy/">caddy</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/charles/">charles</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/collection/">collection</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/cron/">cron</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/framework/">framework</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/hexo/">hexo</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/https/">https</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/ihost/">ihost</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/iterm2/">iterm2</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/jdbc/">jdbc</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mackup/">mackup</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/macos/">macos</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/mybatis/">mybatis</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/phoenix/">phoenix</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/pl/">pl</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/shell/">shell</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/spring/">spring</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/springboot/">springboot</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/tool/">tool</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/zsh/">zsh</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/工具/">工具</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/瞎折腾/">瞎折腾</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/踩坑记/">踩坑记</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/迷思/">迷思</a></li></ul>
                    </div>
                </section>
                
                
                
                <section class="switch-part switch-part3">
                    <div id="js-friends">
                    
                      <a class="main-nav-link switch-friends-link" href="https://hexo.io">Hexo</a>
                    
                      <a class="main-nav-link switch-friends-link" href="https://pages.github.com/">GitHub</a>
                    
                      <a class="main-nav-link switch-friends-link" href="http://moxfive.xyz/">MOxFIVE</a>
                    
                    </div>
                </section>
                

                
                
                <section class="switch-part switch-part4">
                
                    <div id="js-aboutme">天地有万古，此身不再得；人生只百年，此日最易过。幸生其间者，不可不知有生之乐；亦不可不怀虚生之忧。</div>
                </section>
                
            </div>
        </div>
    </header>                
</div>
    </div>
    <div class="mid-col">
      <nav id="mobile-nav">
      <div class="overlay">
          <div class="slider-trigger"></div>
          <h1 class="header-author js-mobile-header hide"><a href="/" title="回到主页">孤星可</a></h1>
      </div>
    <div class="intrude-less">
        <header id="header" class="inner">
            <a href="/" class="profilepic">
                <img src="/img/avatar.png" class="animated zoomIn">
            </a>
            <hgroup>
              <h1 class="header-author"><a href="/" title="回到主页">孤星可</a></h1>
            </hgroup>
            
            <p class="header-subtitle">一个程序员</p>
            
            <nav class="header-menu">
                <ul>
                
                    <li><a href="/">主页</a></li>
                
                    <li><a href="/archives/">所有文章</a></li>
                
                    <li><a href="/moment/">时刻</a></li>
                
                    <li><a href="/tags/">标签云</a></li>
                
                    <li><a href="/about/">关于我</a></li>
                
                <div class="clearfix"></div>
                </ul>
            </nav>
            <nav class="header-nav">
                        <ul class="social">
                            
                                <a class="fa Email" target="_blank" href="mailto:admin@guxingke.com" title="Email"></a>
                            
                                <a class="fa GitHub" target="_blank" href="https://github.com/guxingke" title="GitHub"></a>
                            
                                <a class="fa RSS" target="_blank" href="/atom.xml" title="RSS"></a>
                            
                        </ul>
            </nav>
        </header>                
    </div>
    <link class="menu-list" tags="标签" friends="友情链接" about="关于我"/>
</nav>
      <div class="body-wrap"><article id="post-bian-cheng-de-zhi-hui" class="article article-type-post" itemscope itemprop="blogPost">
  
    <div class="article-meta">
      <a href="/2017/04/12/bian-cheng-de-zhi-hui/" class="article-date">
      <time datetime="2017-04-12T04:08:44.000Z" itemprop="datePublished">2017-04-12</time>
</a>


    </div>
  
  <div class="article-inner">
    
      <input type="hidden" class="isFancy" />
    
    
      <header class="article-header">
        
  
    <h1 class="article-title" itemprop="name">
      编程的智慧
    </h1>
  

      </header>
      
      <div class="article-info article-info-post">
        
    <div class="article-category tagcloud">
    <a class="article-category-link" href="/categories/收藏/">收藏</a>
    </div>


        
    <div class="article-tag tagcloud">
        <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/collection/">collection</a></li></ul>
    </div>

        <div class="clearfix"></div>
      </div>
      
    
    <div class="article-entry" itemprop="articleBody">
      
          
        <hr>
<blockquote>
<p>原文: <a href="http://www.yinwang.org/blog-cn/2015/11/21/programming-philosophy" target="_blank" rel="external">编程的智慧</a></p>
<hr>
</blockquote>
<h2 id="编程的智慧"><a href="#编程的智慧" class="headerlink" title="编程的智慧"></a>编程的智慧</h2><p>编程是一种创造性的工作，是一门艺术。精通任何一门艺术，都需要很多的练习和领悟，所以这里提出的“智慧”，并不是号称一天瘦十斤的减肥药，它并不能代替你自己的勤奋。然而由于软件行业喜欢标新立异，喜欢把简单的事情搞复杂，我希望这些文字能给迷惑中的人们指出一些正确的方向，让他们少走一些弯路，基本做到一分耕耘一分收获。</p>
<a id="more"></a>
<h3 id="反复推敲代码"><a href="#反复推敲代码" class="headerlink" title="反复推敲代码"></a>反复推敲代码</h3><p>既然“天才是百分之一的灵感，百分之九十九的汗水”，那我先来谈谈这汗水的部分吧。有人问我，提高编程水平最有效的办法是什么？我想了很久，终于发现最有效的办法，其实是反反复复地修改和推敲代码。</p>
<p>在IU的时候，由于Dan Friedman的严格教导，我们以写出冗长复杂的代码为耻。如果你代码多写了几行，这老顽童就会大笑，说：“当年我解决这个问题，只写了5行代码，你回去再想想吧……” 当然，有时候他只是夸张一下，故意刺激你的，其实没有人能只用5行代码完成。然而这种提炼代码，减少冗余的习惯，却由此深入了我的骨髓。</p>
<p>有些人喜欢炫耀自己写了多少多少万行的代码，仿佛代码的数量是衡量编程水平的标准。然而，如果你总是匆匆写出代码，却从来不回头去推敲，修改和提炼，其实是不可能提高编程水平的。你会制造出越来越多平庸甚至糟糕的代码。在这种意义上，很多人所谓的“工作经验”，跟他代码的质量，其实不一定成正比。如果有几十年的工作经验，却从来不回头去提炼和反思自己的代码，那么他也许还不如一个只有一两年经验，却喜欢反复推敲，仔细领悟的人。</p>
<p>有位文豪说得好：“看一个作家的水平，不是看他发表了多少文字，而要看他的废纸篓里扔掉了多少。” 我觉得同样的理论适用于编程。好的程序员，他们删掉的代码，比留下来的还要多很多。如果你看见一个人写了很多代码，却没有删掉多少，那他的代码一定有很多垃圾。</p>
<p>就像文学作品一样，代码是不可能一蹴而就的。灵感似乎总是零零星星，陆陆续续到来的。任何人都不可能一笔呵成，就算再厉害的程序员，也需要经过一段时间，才能发现最简单优雅的写法。有时候你反复提炼一段代码，觉得到了顶峰，没法再改进了，可是过了几个月再回头来看，又发现好多可以改进和简化的地方。这跟写文章一模一样，回头看几个月或者几年前写的东西，你总能发现一些改进。</p>
<p>所以如果反复提炼代码已经不再有进展，那么你可以暂时把它放下。过几个星期或者几个月再回头来看，也许就有焕然一新的灵感。这样反反复复很多次之后，你就积累起了灵感和智慧，从而能够在遇到新问题的时候直接朝正确，或者接近正确的方向前进。</p>
<h3 id="写优雅的代码"><a href="#写优雅的代码" class="headerlink" title="写优雅的代码"></a>写优雅的代码</h3><p>人们都讨厌“面条代码”（spaghetti code），因为它就像面条一样绕来绕去，没法理清头绪。那么优雅的代码一般是什么形状的呢？经过多年的观察，我发现优雅的代码，在形状上有一些明显的特征。</p>
<p>如果我们忽略具体的内容，从大体结构上来看，优雅的代码看起来就像是一些整整齐齐，套在一起的盒子。如果跟整理房间做一个类比，就很容易理解。如果你把所有物品都丢在一个很大的抽屉里，那么它们就会全都混在一起。你就很难整理，很难迅速的找到需要的东西。但是如果你在抽屉里再放几个小盒子，把物品分门别类放进去，那么它们就不会到处乱跑，你就可以比较容易的找到和管理它们。</p>
<p>优雅的代码的另一个特征是，它的逻辑大体上看起来，是枝丫分明的树状结构（tree）。这是因为程序所做的几乎一切事情，都是信息的传递和分支。你可以把代码看成是一个电路，电流经过导线，分流或者汇合。如果你是这样思考的，你的代码里就会比较少出现只有一个分支的if语句，它看起来就会像这个样子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">if (...) &#123;</div><div class="line">  if (...) &#123;</div><div class="line">    ...</div><div class="line">  &#125; else &#123;</div><div class="line">    ...</div><div class="line">  &#125;</div><div class="line">&#125; else if (...) &#123;</div><div class="line">  ...</div><div class="line">&#125; else &#123;</div><div class="line">  ...</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>注意到了吗？在我的代码里面，if语句几乎总是有两个分支。它们有可能嵌套，有多层的缩进，而且else分支里面有可能出现少量重复的代码。然而这样的结构，逻辑却非常严密和清晰。在后面我会告诉你为什么if语句最好有两个分支。</p>
<h3 id="写模块化的代码"><a href="#写模块化的代码" class="headerlink" title="写模块化的代码"></a>写模块化的代码</h3><p>有些人吵着闹着要让程序“模块化”，结果他们的做法是把代码分部到多个文件和目录里面，然后把这些目录或者文件叫做“module”。他们甚至把这些目录分放在不同的VCS repo里面。结果这样的作法并没有带来合作的流畅，而是带来了许多的麻烦。这是因为他们其实并不理解什么叫做“模块”，肤浅的把代码切割开来，分放在不同的位置，其实非但不能达到模块化的目的，而且制造了不必要的麻烦。</p>
<p>真正的模块化，并不是文本意义上的，而是逻辑意义上的。一个模块应该像一个电路芯片，它有定义良好的输入和输出。实际上一种很好的模块化方法早已经存在，它的名字叫做“函数”。每一个函数都有明确的输入（参数）和输出（返回值），同一个文件里可以包含多个函数，所以你其实根本不需要把代码分开在多个文件或者目录里面，同样可以完成代码的模块化。我可以把代码全都写在同一个文件里，却仍然是非常模块化的代码。</p>
<p>想要达到很好的模块化，你需要做到以下几点：</p>
<ul>
<li>避免写太长的函数。如果发现函数太大了，就应该把它拆分成几个更小的。通常我写的函数长度都不超过40行。对比一下，一般笔记本电脑屏幕所能容纳的代码行数是50行。我可以一目了然的看见一个40行的函数，而不需要滚屏。只有40行而不是50行的原因是，我的眼球不转的话，最大的视角只看得到40行代码。<br>如果我看代码不转眼球的话，我就能把整片代码完整的映射到我的视觉神经里，这样就算忽然闭上眼睛，我也能看得见这段代码。我发现闭上眼睛的时候，大脑能够更加有效地处理代码，你能想象这段代码可以变成什么其它的形状。40行并不是一个很大的限制，因为函数里面比较复杂的部分，往往早就被我提取出去，做成了更小的函数，然后从原来的函数里面调用。</li>
<li>制造小的工具函数。如果你仔细观察代码，就会发现其实里面有很多的重复。这些常用的代码，不管它有多短，提取出去做成函数，都可能是会有好处的。有些帮助函数也许就只有两行，然而它们却能大大简化主要函数里面的逻辑。<br>有些人不喜欢使用小的函数，因为他们想避免函数调用的开销，结果他们写出几百行之大的函数。这是一种过时的观念。现代的编译器都能自动的把小的函数内联（inline）到调用它的地方，所以根本不产生函数调用，也就不会产生任何多余的开销。<br>同样的一些人，也爱使用宏（macro）来代替小函数，这也是一种过时的观念。在早期的C语言编译器里，只有宏是静态“内联”的，所以他们使用宏，其实是为了达到内联的目的。然而能否内联，其实并不是宏与函数的根本区别。宏与函数有着巨大的区别（这个我以后再讲），应该尽量避免使用宏。为了内联而使用宏，其实是滥用了宏，这会引起各种各样的麻烦，比如使程序难以理解，难以调试，容易出错等等。</li>
<li>每个函数只做一件简单的事情。有些人喜欢制造一些“通用”的函数，既可以做这个又可以做那个，它的内部依据某些变量和条件，来“选择”这个函数所要做的事情。比如，你也许写出这样的函数：<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">void foo() &#123;</div><div class="line">  if (getOS().equals(&quot;MacOS&quot;)) &#123;</div><div class="line">    a();</div><div class="line">  &#125; else &#123;</div><div class="line">    b();</div><div class="line">  &#125;</div><div class="line">  c();</div><div class="line">  if (getOS().equals(&quot;MacOS&quot;)) &#123;</div><div class="line">    d();</div><div class="line">  &#125; else &#123;</div><div class="line">    e();</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
</li>
</ul>
<p>写这个函数的人，根据系统是否为“MacOS”来做不同的事情。你可以看出这个函数里，其实只有c()是两种系统共有的，而其它的a(), b(), d(), e()都属于不同的分支。<br>这种“复用”其实是有害的。如果一个函数可能做两种事情，它们之间共同点少于它们的不同点，那你最好就写两个不同的函数，否则这个函数的逻辑就不会很清晰，容易出现错误。其实，上面这个函数可以改写成两个函数：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">void fooMacOS() &#123;</div><div class="line">  a();</div><div class="line">  c();</div><div class="line">  d();</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>和<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">void fooOther() &#123;</div><div class="line">  b();</div><div class="line">  c();</div><div class="line">  e();</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>如果你发现两件事情大部分内容相同，只有少数不同，多半时候你可以把相同的部分提取出去，做成一个辅助函数。比如，如果你有个函数是这样：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">void foo() &#123;</div><div class="line">  a();</div><div class="line">  b()</div><div class="line">  c();</div><div class="line">  if (getOS().equals(&quot;MacOS&quot;)) &#123;</div><div class="line">    d();</div><div class="line">  &#125; else &#123;</div><div class="line">    e();</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>其中a()，b()，c()都是一样的，只有d()和e()根据系统有所不同。那么你可以把a()，b()，c()提取出去：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">void preFoo() &#123;</div><div class="line">  a();</div><div class="line">  b()</div><div class="line">  c();</div></pre></td></tr></table></figure></p>
<p>然后制造两个函数：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">void fooMacOS() &#123;</div><div class="line">  preFoo();</div><div class="line">  d();</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>和<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">void fooOther() &#123;</div><div class="line">  preFoo();</div><div class="line">  e();</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>这样一来，我们既共享了代码，又做到了每个函数只做一件简单的事情。这样的代码，逻辑就更加清晰。</p>
<ul>
<li>避免使用全局变量和类成员（class member）来传递信息，尽量使用局部变量和参数。有些人写代码，经常用类成员来传递信息，就像这样：<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">class A &#123;</div><div class="line">  String x;</div><div class="line">  void findX() &#123;</div><div class="line">     ...</div><div class="line">     x = ...;</div><div class="line">  &#125;</div><div class="line">  void foo() &#123;</div><div class="line">    findX();</div><div class="line">    ...</div><div class="line">    print(x);</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
</li>
</ul>
<p>首先，他使用findX()，把一个值写入成员x。然后，使用x的值。这样，x就变成了findX和print之间的数据通道。由于x属于class A，这样程序就失去了模块化的结构。由于这两个函数依赖于成员x，它们不再有明确的输入和输出，而是依赖全局的数据。findX和foo不再能够离开class A而存在，而且由于类成员还有可能被其他代码改变，代码变得难以理解，难以确保正确性。<br>如果你使用局部变量而不是类成员来传递信息，那么这两个函数就不需要依赖于某一个class，而且更加容易理解，不易出错：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">String findX() &#123;</div><div class="line">   ...</div><div class="line">   x = ...;</div><div class="line">   return x;</div><div class="line">&#125;</div><div class="line">void foo() &#123;</div><div class="line">  String x = findX();</div><div class="line">  print(x);</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<h3 id="写可读的代码"><a href="#写可读的代码" class="headerlink" title="写可读的代码"></a>写可读的代码</h3><p>有些人以为写很多注释就可以让代码更加可读，然而却发现事与愿违。注释不但没能让代码变得可读，反而由于大量的注释充斥在代码中间，让程序变得障眼难读。而且代码的逻辑一旦修改，就会有很多的注释变得过时，需要更新。修改注释是相当大的负担，所以大量的注释，反而成为了妨碍改进代码的绊脚石。</p>
<p>实际上，真正优雅可读的代码，是几乎不需要注释的。如果你发现需要写很多注释，那么你的代码肯定是含混晦涩，逻辑不清晰的。其实，程序语言相比自然语言，是更加强大而严谨的，它其实具有自然语言最主要的元素：主语，谓语，宾语，名词，动词，如果，那么，否则，是，不是，…… 所以如果你充分利用了程序语言的表达能力，你完全可以用程序本身来表达它到底在干什么，而不需要自然语言的辅助。</p>
<p>有少数的时候，你也许会为了绕过其他一些代码的设计问题，采用一些违反直觉的作法。这时候你可以使用很短注释，说明为什么要写成那奇怪的样子。这样的情况应该少出现，否则这意味着整个代码的设计都有问题。</p>
<p>如果没能合理利用程序语言提供的优势，你会发现程序还是很难懂，以至于需要写注释。所以我现在告诉你一些要点，也许可以帮助你大大减少写注释的必要：</p>
<ol>
<li>使用有意义的函数和变量名字。如果你的函数和变量的名字，能够切实的描述它们的逻辑，那么你就不需要写注释来解释它在干什么。比如：<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">// put elephant1 into fridge2</div><div class="line">put(elephant1, fridge2);</div></pre></td></tr></table></figure>
</li>
</ol>
<p>由于我的函数名put，加上两个有意义的变量名elephant1和fridge2，已经说明了这是在干什么（把大象放进冰箱），所以上面那句注释完全没有必要。</p>
<ol>
<li>局部变量应该尽量接近使用它的地方。有些人喜欢在函数最开头定义很多局部变量，然后在下面很远的地方使用它，就像这个样子：<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">void foo() &#123;</div><div class="line">  int index = ...;</div><div class="line">  ...</div><div class="line">  ...</div><div class="line">  bar(index);</div><div class="line">  ...</div><div class="line">&#125;</div></pre></td></tr></table></figure>
</li>
</ol>
<p>由于这中间都没有使用过index，也没有改变过它所依赖的数据，所以这个变量定义，其实可以挪到接近使用它的地方：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">void foo() &#123;</div><div class="line">  ...</div><div class="line">  ...</div><div class="line">  int index = ...;</div><div class="line">  bar(index);</div><div class="line">  ...</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>这样读者看到bar(index)，不需要向上看很远就能发现index是如何算出来的。而且这种短距离，可以加强读者对于这里的“计算顺序”的理解。否则如果index在顶上，读者可能会怀疑，它其实保存了某种会变化的数据，或者它后来又被修改过。如果index放在下面，读者就清楚的知道，index并不是保存了什么可变的值，而且它算出来之后就没变过。<br>如果你看透了局部变量的本质——它们就是电路里的导线，那你就能更好的理解近距离的好处。变量定义离用的地方越近，导线的长度就越短。你不需要摸着一根导线，绕来绕去找很远，就能发现接收它的端口，这样的电路就更容易理解。</p>
<ol>
<li>局部变量名字应该简短。这貌似跟第一点相冲突，简短的变量名怎么可能有意义呢？注意我这里说的是局部变量，因为它们处于局部，再加上第2点已经把它放到离使用位置尽量近的地方，所以根据上下文你就会容易知道它的意思：<br>比如，你有一个局部变量，表示一个操作是否成功：<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">boolean successInDeleteFile = deleteFile(&quot;foo.txt&quot;);</div><div class="line">if (successInDeleteFile) &#123;</div><div class="line">  ...</div><div class="line">&#125; else &#123;</div><div class="line">  ...</div><div class="line">&#125;</div></pre></td></tr></table></figure>
</li>
</ol>
<p>这个局部变量successInDeleteFile大可不必这么啰嗦。因为它只用过一次，而且用它的地方就在下面一行，所以读者可以轻松发现它是deleteFile返回的结果。如果你把它改名为success，其实读者根据一点上下文，也知道它表示”success in deleteFile”。所以你可以把它改成这样：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">boolean success = deleteFile(&quot;foo.txt&quot;);</div><div class="line">if (success) &#123;</div><div class="line">  ...</div><div class="line">&#125; else &#123;</div><div class="line">  ...</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>这样的写法不但没漏掉任何有用的语义信息，而且更加易读。successInDeleteFile这种“<a href="https://en.wikipedia.org/wiki/CamelCase" target="_blank" rel="external">camelCase</a>”，如果超过了三个单词连在一起，其实是很碍眼的东西。所以如果你能用一个单词表示同样的意义，那当然更好。</p>
<ol>
<li>不要重用局部变量。很多人写代码不喜欢定义新的局部变量，而喜欢“重用”同一个局部变量，通过反复对它们进行赋值，来表示完全不同意思。比如这样写：<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">String msg;</div><div class="line">if (...) &#123;</div><div class="line">  msg = &quot;succeed&quot;;</div><div class="line">  log.info(msg);</div><div class="line">&#125; else &#123;</div><div class="line">  msg = &quot;failed&quot;;</div><div class="line">  log.info(msg);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
</li>
</ol>
<p>虽然这样在逻辑上是没有问题的，然而却不易理解，容易混淆。变量msg两次被赋值，表示完全不同的两个值。它们立即被log.info使用，没有传递到其它地方去。这种赋值的做法，把局部变量的作用域不必要的增大，让人以为它可能在将来改变，也许会在其它地方被使用。更好的做法，其实是定义两个变量：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">if (...) &#123;</div><div class="line">  String msg = &quot;succeed&quot;;</div><div class="line">  log.info(msg);</div><div class="line">&#125; else &#123;</div><div class="line">  String msg = &quot;failed&quot;;</div><div class="line">  log.info(msg);</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>由于这两个msg变量的作用域仅限于它们所处的if语句分支，你可以很清楚的看到这两个msg被使用的范围，而且知道它们之间没有任何关系。</p>
<ol>
<li>把复杂的逻辑提取出去，做成“帮助函数”。有些人写的函数很长，以至于看不清楚里面的语句在干什么，所以他们误以为需要写注释。如果你仔细观察这些代码，就会发现不清晰的那片代码，往往可以被提取出去，做成一个函数，然后在原来的地方调用。由于函数有一个名字，这样你就可以使用有意义的函数名来代替注释。举一个例子：<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">...</div><div class="line">// put elephant1 into fridge2</div><div class="line">openDoor(fridge2);</div><div class="line">if (elephant1.alive()) &#123;</div><div class="line">  ...</div><div class="line">&#125; else &#123;</div><div class="line">   ...</div><div class="line">&#125;</div><div class="line">closeDoor(fridge2);</div><div class="line">...</div></pre></td></tr></table></figure>
</li>
</ol>
<p>如果你把这片代码提出去定义成一个函数：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">void put(Elephant elephant, Fridge fridge) &#123;</div><div class="line">  openDoor(fridge);</div><div class="line">  if (elephant.alive()) &#123;</div><div class="line">    ...</div><div class="line">  &#125; else &#123;</div><div class="line">     ...</div><div class="line">  &#125;</div><div class="line">  closeDoor(fridge);</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>这样原来的代码就可以改成：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">...</div><div class="line">put(elephant1, fridge2);</div><div class="line">...</div></pre></td></tr></table></figure></p>
<p>更加清晰，而且注释也没必要了。</p>
<ol>
<li>把复杂的表达式提取出去，做成中间变量。有些人听说“函数式编程”是个好东西，也不理解它的真正含义，就在代码里大量使用嵌套的函数。像这样：<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">Pizza pizza = makePizza(crust(salt(), butter()),</div><div class="line">   topping(onion(), tomato(), sausage()));</div></pre></td></tr></table></figure>
</li>
</ol>
<p>这样的代码一行太长，而且嵌套太多，不容易看清楚。其实训练有素的函数式程序员，都知道中间变量的好处，不会盲目的使用嵌套的函数。他们会把这代码变成这样：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">Crust crust = crust(salt(), butter());</div><div class="line">Topping topping = topping(onion(), tomato(), sausage());</div><div class="line">Pizza pizza = makePizza(crust, topping);</div></pre></td></tr></table></figure></p>
<p>这样写，不但有效地控制了单行代码的长度，而且由于引入的中间变量具有“意义”，步骤清晰，变得很容易理解。</p>
<ol>
<li>在合理的地方换行。对于绝大部分的程序语言，代码的逻辑是和空白字符无关的，所以你可以在几乎任何地方换行，你也可以不换行。这样的语言设计是个好东西，因为它给了程序员自由控制自己代码格式的能力。然而，它也引起了一些问题，因为很多人不知道如何合理的换行。</li>
</ol>
<p>有些人喜欢利用IDE的自动换行机制，编辑之后用一个热键把整个代码重新格式化一遍，IDE就会把超过行宽限制的代码自动折行。可是这种自动这行，往往没有根据代码的逻辑来进行，不能帮助理解代码。自动换行之后可能产生这样的代码：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">if (someLongCondition1() &amp;&amp; someLongCondition2() &amp;&amp; someLongCondition3() &amp;&amp;</div><div class="line">  someLongCondition4()) &#123;</div><div class="line">  ...</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>由于someLongCondition4()超过了行宽限制，被编辑器自动换到了下面一行。虽然满足了行宽限制，换行的位置却是相当任意的，它并不能帮助人理解这代码的逻辑。这几个boolean表达式，全都用&amp;&amp;连接，所以它们其实处于平等的地位。为了表达这一点，当需要折行的时候，你应该把每一个表达式都放到新的一行，就像这个样子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">if (someLongCondition1() &amp;&amp;</div><div class="line">    someLongCondition2() &amp;&amp;</div><div class="line">    someLongCondition3() &amp;&amp;</div><div class="line">    someLongCondition4()) &#123;</div><div class="line">  ...</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>这样每一个条件都对齐，里面的逻辑就很清楚了。再举个例子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">log.info(&quot;failed to find file &#123;&#125; for command &#123;&#125;, with exception &#123;&#125;&quot;, file, command,</div><div class="line">  exception);</div></pre></td></tr></table></figure>
<p>这行因为太长，被自动折行成这个样子。file，command和exception本来是同一类东西，却有两个留在了第一行，最后一个被折到第二行。它就不如手动换行成这个样子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">log.info(&quot;failed to find file &#123;&#125; for command &#123;&#125;, with exception &#123;&#125;&quot;,</div><div class="line">  file, command, exception);</div></pre></td></tr></table></figure>
<p>把格式字符串单独放在一行，而把它的参数一并放在另外一行，这样逻辑就更加清晰。</p>
<p>为了避免IDE把这些手动调整好的换行弄乱，很多IDE（比如IntelliJ）的自动格式化设定里都有“保留原来的换行符”的设定。如果你发现IDE的换行不符合逻辑，你可以修改这些设定，然后在某些地方保留你自己的手动换行。</p>
<p>说到这里，我必须警告你，这里所说的“不需注释，让代码自己解释自己”，并不是说要让代码看起来像某种自然语言。有个叫Chai的JavaScript测试工具，可以让你这样写代码：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">expect(foo).to.be.a(&apos;string&apos;);</div><div class="line">expect(foo).to.equal(&apos;bar&apos;);</div><div class="line">expect(foo).to.have.length(3);</div><div class="line">expect(tea).to.have.property(&apos;flavors&apos;).with.length(3);</div></pre></td></tr></table></figure>
<p>这种做法是极其错误的。程序语言本来就比自然语言简单清晰，这种写法让它看起来像自然语言的样子，反而变得复杂难懂了。</p>
<h3 id="写简单的代码"><a href="#写简单的代码" class="headerlink" title="写简单的代码"></a>写简单的代码</h3><p>程序语言都喜欢标新立异，提供这样那样的“特性”，然而有些特性其实并不是什么好东西。很多特性都经不起时间的考验，最后带来的麻烦，比解决的问题还多。很多人盲目的追求“短小”和“精悍”，或者为了显示自己头脑聪明，学得快，所以喜欢利用语言里的一些特殊构造，写出过于“聪明”，难以理解的代码。</p>
<p>并不是语言提供什么，你就一定要把它用上的。实际上你只需要其中很小的一部分功能，就能写出优秀的代码。我一向反对“充分利用”程序语言里的所有特性。实际上，我心目中有一套最好的构造。不管语言提供了多么“神奇”的，“新”的特性，我基本都只用经过千锤百炼，我觉得值得信赖的那一套。</p>
<p>现在针对一些有问题的语言特性，我介绍一些我自己使用的代码规范，并且讲解一下为什么它们能让代码更简单。</p>
<ul>
<li>避免使用自增减表达式（i++，++i，i–，–i）。这种自增减操作表达式其实是历史遗留的设计失误。它们含义蹊跷，非常容易弄错。它们把读和写这两种完全不同的操作，混淆缠绕在一起，把语义搞得乌七八糟。含有它们的表达式，结果可能取决于求值顺序，所以它可能在某种编译器下能正确运行，换一个编译器就出现离奇的错误。<br>其实这两个表达式完全可以分解成两步，把读和写分开：一步更新i的值，另外一步使用i的值。比如，如果你想写foo(i++)，你完全可以把它拆成int t = i; i += 1; foo(t);。如果你想写foo(++i)，可以拆成i += 1; foo(i); 拆开之后的代码，含义完全一致，却清晰很多。到底更新是在取值之前还是之后，一目了然。<br>有人也许以为i++或者++i的效率比拆开之后要高，这只是一种错觉。这些代码经过基本的编译器优化之后，生成的机器代码是完全没有区别的。自增减表达式只有在两种情况下才可以安全的使用。一种是在for循环的update部分，比如for(int i = 0; i &lt; 5; i++)。另一种情况是写成单独的一行，比如i++;。这两种情况是完全没有歧义的。你需要避免其它的情况，比如用在复杂的表达式里面，比如foo(i++)，foo(++i) + foo(i)，…… 没有人应该知道，或者去追究这些是什么意思。</li>
<li>永远不要省略花括号。很多语言允许你在某种情况下省略掉花括号，比如C，Java都允许你在if语句里面只有一句话的时候省略掉花括号：<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">if (...)</div><div class="line">  action1();</div></pre></td></tr></table></figure>
</li>
</ul>
<p>咋一看少打了两个字，多好。可是这其实经常引起奇怪的问题。比如，你后来想要加一句话action2()到这个if里面，于是你就把代码改成：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">if (...)</div><div class="line">  action1();</div><div class="line">  action2();</div></pre></td></tr></table></figure></p>
<p>为了美观，你很小心的使用了action1()的缩进。咋一看它们是在一起的，所以你下意识里以为它们只会在if的条件为真的时候执行，然而action2()却其实在if外面，它会被无条件的执行。我把这种现象叫做“光学幻觉”（optical illusion），理论上每个程序员都应该发现这个错误，然而实际上却容易被忽视。<br>那么你问，谁会这么傻，我在加入action2()的时候加上花括号不就行了？可是从设计的角度来看，这样其实并不是合理的作法。首先，也许你以后又想把action2()去掉，这样你为了样式一致，又得把花括号拿掉，烦不烦啊？其次，这使得代码样式不一致，有的if有花括号，有的又没有。况且，你为什么需要记住这个规则？如果你不问三七二十一，只要是if-else语句，把花括号全都打上，就可以想都不用想了，就当C和Java没提供给你这个特殊写法。这样就可以保持完全的一致性，减少不必要的思考。<br>有人可能会说，全都打上花括号，只有一句话也打上，多碍眼啊？然而经过实行这种编码规范几年之后，我并没有发现这种写法更加碍眼，反而由于花括号的存在，使得代码界限明确，让我的眼睛负担更小了。</p>
<ul>
<li>合理使用括号，不要盲目依赖操作符优先级。利用操作符的优先级来减少括号，对于1 + 2 <em> 3这样常见的算数表达式，是没问题的。然而有些人如此的仇恨括号，以至于他们会写出2 &lt;&lt; 7 - 2 </em> 3这样的表达式，而完全不用括号。<br>这里的问题，在于移位操作&lt;&lt;的优先级，是很多人不熟悉，而且是违反常理的。由于x &lt;&lt; 1相当于把x乘以2，很多人误以为这个表达式相当于(2 &lt;&lt; 7) - (2 <em> 3)，所以等于250。然而实际上&lt;&lt;的优先级比加法+还要低，所以这表达式其实相当于2 &lt;&lt; (7 - 2 </em> 3)，所以等于4！<br>解决这个问题的办法，不是要每个人去把操作符优先级表给硬背下来，而是合理的加入括号。比如上面的例子，最好直接加上括号写成2 &lt;&lt; (7 - 2 * 3)。虽然没有括号也表示同样的意思，但是加上括号就更加清晰，读者不再需要死记&lt;&lt;的优先级就能理解代码。</li>
<li>避免使用continue和break。循环语句（for，while）里面出现return是没问题的，然而如果你使用了continue或者break，就会让循环的逻辑和终止条件变得复杂，难以确保正确。<br>出现continue或者break的原因，往往是对循环的逻辑没有想清楚。如果你考虑周全了，应该是几乎不需要continue或者break的。如果你的循环里出现了continue或者break，你就应该考虑改写这个循环。改写循环的办法有多种：</li>
</ul>
<ol>
<li>如果出现了continue，你往往只需要把continue的条件反向，就可以消除continue。</li>
<li>如果出现了break，你往往可以把break的条件，合并到循环头部的终止条件里，从而去掉break。</li>
<li>有时候你可以把break替换成return，从而去掉break。</li>
<li>如果以上都失败了，你也许可以把循环里面复杂的部分提取出来，做成函数调用，之后continue或者break就可以去掉了。</li>
</ol>
<p>下面我对这些情况举一些例子。<br>情况1：下面这段代码里面有一个continue：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">List&lt;String&gt; goodNames = new ArrayList&lt;&gt;();</div><div class="line">for (String name: names) &#123;</div><div class="line">  if (name.contains(&quot;bad&quot;)) &#123;</div><div class="line">    continue;</div><div class="line">  &#125;</div><div class="line">  goodNames.add(name);</div><div class="line">  ...</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>它说：“如果name含有’bad’这个词，跳过后面的循环代码……” 注意，这是一种“负面”的描述，它不是在告诉你什么时候“做”一件事，而是在告诉你什么时候“不做”一件事。为了知道它到底在干什么，你必须搞清楚continue会导致哪些语句被跳过了，然后脑子里把逻辑反个向，你才能知道它到底想做什么。这就是为什么含有continue和break的循环不容易理解，它们依靠“控制流”来描述“不做什么”，“跳过什么”，结果到最后你也没搞清楚它到底“要做什么”。<br>其实，我们只需要把continue的条件反向，这段代码就可以很容易的被转换成等价的，不含continue的代码：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">List&lt;String&gt; goodNames = new ArrayList&lt;&gt;();</div><div class="line">for (String name: names) &#123;</div><div class="line">  if (!name.contains(&quot;bad&quot;)) &#123;</div><div class="line">    goodNames.add(name);</div><div class="line">    ...</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>goodNames.add(name);和它之后的代码全部被放到了if里面，多了一层缩进，然而continue却没有了。你再读这段代码，就会发现更加清晰。因为它是一种更加“正面”地描述。它说：“在name不含有’bad’这个词的时候，把它加到goodNames的链表里面……”<br>情况2：for和while头部都有一个循环的“终止条件”，那本来应该是这个循环唯一的退出条件。如果你在循环中间有break，它其实给这个循环增加了一个退出条件。你往往只需要把这个条件合并到循环头部，就可以去掉break。<br>比如下面这段代码：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">while (condition1) &#123;</div><div class="line">  ...</div><div class="line">  if (condition2) &#123;</div><div class="line">    break;</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>当condition成立的时候，break会退出循环。其实你只需要把condition2反转之后，放到while头部的终止条件，就可以去掉这种break语句。改写后的代码如下：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">while (condition1 &amp;&amp; !condition2) &#123;</div><div class="line">  ...</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>这种情况表面上貌似只适用于break出现在循环开头或者末尾的时候，然而其实大部分时候，break都可以通过某种方式，移动到循环的开头或者末尾。具体的例子我暂时没有，等出现的时候再加进来。<br>情况3：很多break退出循环之后，其实接下来就是一个return。这种break往往可以直接换成return。比如下面这个例子：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">public boolean hasBadName(List&lt;String&gt; names) &#123;</div><div class="line">    boolean result = false;</div><div class="line">    for (String name: names) &#123;</div><div class="line">        if (name.contains(&quot;bad&quot;)) &#123;</div><div class="line">            result = true;</div><div class="line">            break;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">    return result;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>这个函数检查names链表里是否存在一个名字，包含“bad”这个词。它的循环里包含一个break语句。这个函数可以被改写成：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">public boolean hasBadName(List&lt;String&gt; names) &#123;</div><div class="line">    for (String name: names) &#123;</div><div class="line">        if (name.contains(&quot;bad&quot;)) &#123;</div><div class="line">            return true;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">    return false;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>改进后的代码，在name里面含有“bad”的时候，直接用return true返回，而不是对result变量赋值，break出去，最后才返回。如果循环结束了还没有return，那就返回false，表示没有找到这样的名字。使用return来代替break，这样break语句和result这个变量，都一并被消除掉了。<br>我曾经见过很多其他使用continue和break的例子，几乎无一例外的可以被消除掉，变换后的代码变得清晰很多。我的经验是，99%的break和continue，都可以通过替换成return语句，或者翻转if条件的方式来消除掉。剩下的1%含有复杂的逻辑，但也可以通过提取一个帮助函数来消除掉。修改之后的代码变得容易理解，容易确保正确。</p>
<h3 id="写直观的代码"><a href="#写直观的代码" class="headerlink" title="写直观的代码"></a>写直观的代码</h3><p>我写代码有一条重要的原则：如果有更加直接，更加清晰的写法，就选择它，即使它看起来更长，更笨，也一样选择它。比如，Unix命令行有一种“巧妙”的写法是这样：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">command1 &amp;&amp; command2 &amp;&amp; command3</div></pre></td></tr></table></figure>
<p>由于Shell语言的逻辑操作a &amp;&amp; b具有“短路”的特性，如果a等于false，那么b就没必要执行了。这就是为什么当command1成功，才会执行command2，当command2成功，才会执行command3。同样，</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">command1 || command2 || command3</div></pre></td></tr></table></figure>
<p>操作符||也有类似的特性。上面这个命令行，如果command1成功，那么command2和command3都不会被执行。如果command1失败，command2成功，那么command3就不会被执行。</p>
<p>这比起用if语句来判断失败，似乎更加巧妙和简洁，所以有人就借鉴了这种方式，在程序的代码里也使用这种方式。比如他们可能会写这样的代码：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">if (action1() || action2() &amp;&amp; action3()) &#123;</div><div class="line">  ...</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>你看得出来这代码是想干什么吗？action2和action3什么条件下执行，什么条件下不执行？也许稍微想一下，你知道它在干什么：“如果action1失败了，执行action2，如果action2成功了，执行action3”。然而那种语义，并不是直接的“映射”在这代码上面的。比如“失败”这个词，对应了代码里的哪一个字呢？你找不出来，因为它包含在了||的语义里面，你需要知道||的短路特性，以及逻辑或的语义才能知道这里面在说“如果action1失败……”。每一次看到这行代码，你都需要思考一下，这样积累起来的负荷，就会让人很累。</p>
<p>其实，这种写法是滥用了逻辑操作&amp;&amp;和||的短路特性。这两个操作符可能不执行右边的表达式，原因是为了机器的执行效率，而不是为了给人提供这种“巧妙”的用法。这两个操作符的本意，只是作为逻辑操作，它们并不是拿来给你代替if语句的。也就是说，它们只是碰巧可以达到某些if语句的效果，但你不应该因此就用它来代替if语句。如果你这样做了，就会让代码晦涩难懂。</p>
<p>上面的代码写成笨一点的办法，就会清晰很多：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">if (!action1()) &#123;</div><div class="line">  if (action2()) &#123;</div><div class="line">    action3();</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>这里我很明显的看出这代码在说什么，想都不用想：如果action1()失败了，那么执行action2()，如果action2()成功了，执行action3()。你发现这里面的一一对应关系吗？if=如果，!=失败，…… 你不需要利用逻辑学知识，就知道它在说什么。</p>
<h3 id="写无懈可击的代码"><a href="#写无懈可击的代码" class="headerlink" title="写无懈可击的代码"></a>写无懈可击的代码</h3><p>在之前一节里，我提到了自己写的代码里面很少出现只有一个分支的if语句。我写出的if语句，大部分都有两个分支，所以我的代码很多看起来是这个样子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">if (...) &#123;</div><div class="line">  if (...) &#123;</div><div class="line">    ...</div><div class="line">    return false;</div><div class="line">  &#125; else &#123;</div><div class="line">    return true;</div><div class="line">  &#125;</div><div class="line">&#125; else if (...) &#123;</div><div class="line">  ...</div><div class="line">  return false;</div><div class="line">&#125; else &#123;</div><div class="line">  return true;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>使用这种方式，其实是为了无懈可击的处理所有可能出现的情况，避免漏掉corner case。每个if语句都有两个分支的理由是：如果if的条件成立，你做某件事情；但是如果if的条件不成立，你应该知道要做什么另外的事情。不管你的if有没有else，你终究是逃不掉，必须得思考这个问题的。</p>
<p>很多人写if语句喜欢省略else的分支，因为他们觉得有些else分支的代码重复了。比如我的代码里，两个else分支都是return true。为了避免重复，他们省略掉那两个else分支，只在最后使用一个return true。这样，缺了else分支的if语句，控制流自动“掉下去”，到达最后的return true。他们的代码看起来像这个样子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">if (...) &#123;</div><div class="line">  if (...) &#123;</div><div class="line">    ...</div><div class="line">    return false;</div><div class="line">  &#125;</div><div class="line">&#125; else if (...) &#123;</div><div class="line">  ...</div><div class="line">  return false;</div><div class="line">&#125;</div><div class="line">return true;</div></pre></td></tr></table></figure>
<p>这种写法看似更加简洁，避免了重复，然而却很容易出现疏忽和漏洞。嵌套的if语句省略了一些else，依靠语句的“控制流”来处理else的情况，是很难正确的分析和推理的。如果你的if条件里使用了&amp;&amp;和||之类的逻辑运算，就更难看出是否涵盖了所有的情况。</p>
<p>由于疏忽而漏掉的分支，全都会自动“掉下去”，最后返回意想不到的结果。即使你看一遍之后确信是正确的，每次读这段代码，你都不能确信它照顾了所有的情况，又得重新推理一遍。这简洁的写法，带来的是反复的，沉重的头脑开销。这就是所谓“面条代码”，因为程序的逻辑分支，不是像一棵枝叶分明的树，而是像面条一样绕来绕去。</p>
<p>另外一种省略else分支的情况是这样：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">String s = &quot;&quot;;</div><div class="line">if (x &lt; 5) &#123;</div><div class="line">  s = &quot;ok&quot;;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>写这段代码的人，脑子里喜欢使用一种“缺省值”的做法。s缺省为null，如果x&lt;5，那么把它改变（mutate）成“ok”。这种写法的缺点是，当x&lt;5不成立的时候，你需要往上面看，才能知道s的值是什么。这还是你运气好的时候，因为s就在上面不远。很多人写这种代码的时候，s的初始值离判断语句有一定的距离，中间还有可能插入一些其它的逻辑和赋值操作。这样的代码，把变量改来改去的，看得人眼花，就容易出错。</p>
<p>现在比较一下我的写法：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">String s;</div><div class="line">if (x &lt; 5) &#123;</div><div class="line">  s = &quot;ok&quot;;</div><div class="line">&#125; else &#123;</div><div class="line">  s = &quot;&quot;;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>这种写法貌似多打了一两个字，然而它却更加清晰。这是因为我们明确的指出了x&lt;5不成立的时候，s的值是什么。它就摆在那里，它是””（空字符串）。注意，虽然我也使用了赋值操作，然而我并没有“改变”s的值。s一开始的时候没有值，被赋值之后就再也没有变过。我的这种写法，通常被叫做更加“函数式”，因为我只赋值一次。</p>
<p>如果我漏写了else分支，Java编译器是不会放过我的。它会抱怨：“在某个分支，s没有被初始化。”这就强迫我清清楚楚的设定各种条件下s的值，不漏掉任何一种情况。</p>
<p>当然，由于这个情况比较简单，你还可以把它写成这样：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">String s = x &lt; 5 ? &quot;ok&quot; : &quot;&quot;;</div></pre></td></tr></table></figure>
<p>对于更加复杂的情况，我建议还是写成if语句为好。</p>
<h3 id="正确处理错误"><a href="#正确处理错误" class="headerlink" title="正确处理错误"></a>正确处理错误</h3><p>使用有两个分支的if语句，只是我的代码可以达到无懈可击的其中一个原因。这样写if语句的思路，其实包含了使代码可靠的一种通用思想：穷举所有的情况，不漏掉任何一个。</p>
<p>程序的绝大部分功能，是进行信息处理。从一堆纷繁复杂，模棱两可的信息中，排除掉绝大部分“干扰信息”，找到自己需要的那一个。正确地对所有的“可能性”进行推理，就是写出无懈可击代码的核心思想。这一节我来讲一讲，如何把这种思想用在错误处理上。</p>
<p>错误处理是一个古老的问题，可是经过了几十年，还是很多人没搞明白。Unix的系统API手册，一般都会告诉你可能出现的返回值和错误信息。比如，Linux的<a href="http://man7.org/linux/man-pages/man2/read.2.html" target="_blank" rel="external">read</a>系统调用手册里面有如下内容：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">RETURN VALUE  On success, the number of bytes read is returned... On error, -1 is returned, and errno is set appropriately. ERRORS EAGAIN, EBADF, EFAULT, EINTR, EINVAL, ...</div></pre></td></tr></table></figure>
<p>很多初学者，都会忘记检查read的返回值是否为-1，觉得每次调用read都得检查返回值真繁琐，不检查貌似也相安无事。这种想法其实是很危险的。如果函数的返回值告诉你，要么返回一个正数，表示读到的数据长度，要么返回-1，那么你就必须要对这个-1作出相应的，有意义的处理。千万不要以为你可以忽视这个特殊的返回值，因为它是一种“可能性”。代码漏掉任何一种可能出现的情况，都可能产生意想不到的灾难性结果。</p>
<p>对于Java来说，这相对方便一些。Java的函数如果出现问题，一般通过异常（exception）来表示。你可以把异常加上函数本来的返回值，看成是一个“union类型”。比如：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">String foo() throws MyException &#123;</div><div class="line">  ...</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>这里MyException是一个错误返回。你可以认为这个函数返回一个union类型：{String, MyException}。任何调用foo的代码，必须对MyException作出合理的处理，才有可能确保程序的正确运行。Union类型是一种相当先进的类型，目前只有极少数语言（比如Typed Racket）具有这种类型，我在这里提到它，只是为了方便解释概念。掌握了概念之后，你其实可以在头脑里实现一个union类型系统，这样使用普通的语言也能写出可靠的代码。</p>
<p>由于Java的类型系统强制要求函数在类型里面声明可能出现的异常，而且强制调用者处理可能出现的异常，所以基本上不可能出现由于疏忽而漏掉的情况。但有些Java程序员有一种恶习，使得这种安全机制几乎完全失效。每当编译器报错，说“你没有catch这个foo函数可能出现的异常”时，有些人想都不想，直接把代码改成这样：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">try &#123;</div><div class="line">  foo();</div><div class="line">&#125; catch (Exception e) &#123;&#125;</div></pre></td></tr></table></figure>
<p>或者最多在里面放个log，或者干脆把自己的函数类型上加上throws Exception，这样编译器就不再抱怨。这些做法貌似很省事，然而都是错误的，你终究会为此付出代价。</p>
<p>如果你把异常catch了，忽略掉，那么你就不知道foo其实失败了。这就像开车时看到路口写着“前方施工，道路关闭”，还继续往前开。这当然迟早会出问题，因为你根本不知道自己在干什么。</p>
<p>catch异常的时候，你不应该使用Exception这么宽泛的类型。你应该正好catch可能发生的那种异常A。使用宽泛的异常类型有很大的问题，因为它会不经意的catch住另外的异常（比如B）。你的代码逻辑是基于判断A是否出现，可你却catch所有的异常（Exception类），所以当其它的异常B出现的时候，你的代码就会出现莫名其妙的问题，因为你以为A出现了，而其实它没有。这种bug，有时候甚至使用debugger都难以发现。</p>
<p>如果你在自己函数的类型加上throws Exception，那么你就不可避免的需要在调用它的地方处理这个异常，如果调用它的函数也写着throws Exception，这毛病就传得更远。我的经验是，尽量在异常出现的当时就作出处理。否则如果你把它返回给你的调用者，它也许根本不知道该怎么办了。</p>
<p>另外，try { … } catch里面，应该包含尽量少的代码。比如，如果foo和bar都可能产生异常A，你的代码应该尽可能写成：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">try &#123;</div><div class="line">  foo();</div><div class="line">&#125; catch (A e) &#123;...&#125;</div><div class="line"></div><div class="line">try &#123;</div><div class="line">  bar();</div><div class="line">&#125; catch (A e) &#123;...&#125;</div></pre></td></tr></table></figure>
<p>而不是</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">try &#123;</div><div class="line">  foo();</div><div class="line">  bar();</div><div class="line">&#125; catch (A e) &#123;...&#125;</div></pre></td></tr></table></figure>
<p>第一种写法能明确的分辨是哪一个函数出了问题，而第二种写法全都混在一起。明确的分辨是哪一个函数出了问题，有很多的好处。比如，如果你的catch代码里面包含log，它可以提供给你更加精确的错误信息，这样会大大地加速你的调试过程。</p>
<h3 id="正确处理null指针"><a href="#正确处理null指针" class="headerlink" title="正确处理null指针"></a>正确处理null指针</h3><p>穷举的思想是如此的有用，依据这个原理，我们可以推出一些基本原则，它们可以让你无懈可击的处理null指针。</p>
<p>首先你应该知道，许多语言（C，C++，Java，C#，……）的类型系统对于null的处理，其实是完全错误的。这个错误源自于<a href="https://en.wikipedia.org/wiki/Tony_Hoare" target="_blank" rel="external">Tony Hoare</a>最早的设计，Hoare把这个错误称为自己的“<a href="http://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare" target="_blank" rel="external">billion dollar mistake</a>”，因为由于它所产生的财产和人力损失，远远超过十亿美元。</p>
<p>这些语言的类型系统允许null出现在任何对象（指针）类型可以出现的地方，然而null其实根本不是一个合法的对象。它不是一个String，不是一个Integer，也不是一个自定义的类。null的类型本来应该是NULL，也就是null自己。根据这个基本观点，我们推导出以下原则：</p>
<ul>
<li>尽量不要产生null指针。尽量不要用null来初始化变量，函数尽量不要返回null。如果你的函数要返回“没有”，“出错了”之类的结果，尽量使用Java的异常机制。虽然写法上有点别扭，然而Java的异常，和函数的返回值合并在一起，基本上可以当成union类型来用。比如，如果你有一个函数find，可以帮你找到一个String，也有可能什么也找不到，你可以这样写：<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">public String find() throws NotFoundException &#123;</div><div class="line">  if (...) &#123;</div><div class="line">    return ...;</div><div class="line">  &#125; else &#123;</div><div class="line">    throw new NotFoundException();</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
</li>
</ul>
<p>Java的类型系统会强制你catch这个NotFoundException，所以你不可能像漏掉检查null一样，漏掉这种情况。Java的异常也是一个比较容易滥用的东西，不过我已经在上一节告诉你如何正确的使用异常。<br>Java的try…catch语法相当的繁琐和蹩脚，所以如果你足够小心的话，像find这类函数，也可以返回null来表示“没找到”。这样稍微好看一些，因为你调用的时候不必用try…catch。很多人写的函数，返回null来表示“出错了”，这其实是对null的误用。“出错了”和“没有”，其实完全是两码事。“没有”是一种很常见，正常的情况，比如查哈希表没找到，很正常。“出错了”则表示罕见的情况，本来正常情况下都应该存在有意义的值，偶然出了问题。如果你的函数要表示“出错了”，应该使用异常，而不是null。</p>
<ul>
<li>不要catch NullPointerException。有些人写代码很nice，他们喜欢“容错”。首先他们写一些函数，这些函数里面不大小心，没检查null指针：<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">void foo() &#123;</div><div class="line">  String found = find();</div><div class="line">  int len = found.length();</div><div class="line">  ...</div><div class="line">&#125;</div></pre></td></tr></table></figure>
</li>
</ul>
<p>当foo调用产生了异常，他们不管三七二十一，就把调用的地方改成这样：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">try &#123;</div><div class="line">  foo();</div><div class="line">&#125; catch (Exception e) &#123;</div><div class="line">  ...</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>这样当found是null的时候，NullPointerException就会被捕获并且得到处理。这其实是很错误的作法。首先，上一节已经提到了，catch (Exception e)这种写法是要绝对避免的，因为它捕获所有的异常，包括NullPointerException。这会让你意外地捕获try语句里面出现的NullPointerException，从而把代码的逻辑搅得一塌糊涂。<br>另外就算你写成catch (NullPointerException e)也是不可以的。由于foo的内部缺少了null检查，才出现了NullPointerException。现在你不对症下药，倒把每个调用它的地方加上catch，以后你的生活就会越来越苦。正确的做法应该是改动foo，而不改调用它的代码。foo应该被改成这样：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">void foo() &#123;</div><div class="line">  String found = find();</div><div class="line">  if (found != null) &#123;</div><div class="line">    int len = found.length();</div><div class="line">    ...</div><div class="line">  &#125; else &#123;</div><div class="line">    ...</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>在null可能出现的当时就检查它是否是null，然后进行相应的处理。</p>
<ul>
<li>不要把null放进“容器数据结构”里面。所谓容器（collection），是指一些对象以某种方式集合在一起，所以null不应该被放进Array，List，Set等结构，不应该出现在Map的key或者value里面。把null放进容器里面，是一些莫名其妙错误的来源。因为对象在容器里的位置一般是动态决定的，所以一旦null从某个入口跑进去了，你就很难再搞明白它去了哪里，你就得被迫在所有从这个容器里取值的位置检查null。你也很难知道到底是谁把它放进去的，代码多了就导致调试极其困难。<br>解决方案是：如果你真要表示“没有”，那你就干脆不要把它放进去（Array，List，Set没有元素，Map根本没那个entry），或者你可以指定一个特殊的，真正合法的对象，用来表示“没有”。<br>需要指出的是，类对象并不属于容器。所以null在必要的时候，可以作为对象成员的值，表示它不存在。比如：<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">class A &#123;</div><div class="line">  String name = null;</div><div class="line">  ...</div><div class="line">&#125;</div></pre></td></tr></table></figure>
</li>
</ul>
<p>之所以可以这样，是因为null只可能在A对象的name成员里出现，你不用怀疑其它的成员因此成为null。所以你每次访问name成员时，检查它是否是null就可以了，不需要对其他成员也做同样的检查。</p>
<ul>
<li>函数调用者：明确理解null所表示的意义，尽早检查和处理null返回值，减少它的传播。null很讨厌的一个地方，在于它在不同的地方可能表示不同的意义。有时候它表示“没有”，“没找到”。有时候它表示“出错了”，“失败了”。有时候它甚至可以表示“成功了”，…… 这其中有很多误用之处，不过无论如何，你必须理解每一个null的意义，不能给混淆起来。<br>如果你调用的函数有可能返回null，那么你应该在第一时间对null做出“有意义”的处理。比如，上述的函数find，返回null表示“没找到”，那么调用find的代码就应该在它返回的第一时间，检查返回值是否是null，并且对“没找到”这种情况，作出有意义的处理。<br>“有意义”是什么意思呢？我的意思是，使用这函数的人，应该明确的知道在拿到null的情况下该怎么做，承担起责任来。他不应该只是“向上级汇报”，把责任踢给自己的调用者。如果你违反了这一点，就有可能采用一种不负责任，危险的写法：<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">public String foo() &#123;</div><div class="line">  String found = find();</div><div class="line">  if (found == null) &#123;</div><div class="line">    return null;</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
</li>
</ul>
<p>当看到find()返回了null，foo自己也返回null。这样null就从一个地方，游走到了另一个地方，而且它表示另外一个意思。如果你不假思索就写出这样的代码，最后的结果就是代码里面随时随地都可能出现null。到后来为了保护自己，你的每个函数都会写成这样：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">public void foo(A a, B b, C c) &#123;</div><div class="line">  if (a == null) &#123; ... &#125;</div><div class="line">  if (b == null) &#123; ... &#125;</div><div class="line">  if (c == null) &#123; ... &#125;</div><div class="line">  ...</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<ul>
<li>函数作者：明确声明不接受null参数，当参数是null时立即崩溃。不要试图对null进行“容错”，不要让程序继续往下执行。如果调用者使用了null作为参数，那么调用者（而不是函数作者）应该对程序的崩溃负全责。<br>上面的例子之所以成为问题，就在于人们对于null的“容忍态度”。这种“保护式”的写法，试图“容错”，试图“优雅的处理null”，其结果是让调用者更加肆无忌惮的传递null给你的函数。到后来，你的代码里出现一堆堆nonsense的情况，null可以在任何地方出现，都不知道到底是哪里产生出来的。谁也不知道出现了null是什么意思，该做什么，所有人都把null踢给其他人。最后这null像瘟疫一样蔓延开来，到处都是，成为一场噩梦。<br>正确的做法，其实是强硬的态度。你要告诉函数的使用者，我的参数全都不能是null，如果你给我null，程序崩溃了该你自己负责。至于调用者代码里有null怎么办，他自己该知道怎么处理（参考以上几条），不应该由函数作者来操心。<br>采用强硬态度一个很简单的做法是使用Objects.requireNonNull()。它的定义很简单：<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">public static &lt;T&gt; T requireNonNull(T obj) &#123;</div><div class="line">  if (obj == null) &#123;</div><div class="line">    throw new NullPointerException();</div><div class="line">  &#125; else &#123;</div><div class="line">    return obj;</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
</li>
</ul>
<p>你可以用这个函数来检查不想接受null的每一个参数，只要传进来的参数是null，就会立即触发NullPointerException崩溃掉，这样你就可以有效地防止null指针不知不觉传递到其它地方去。</p>
<ul>
<li>使用@NotNull和@Nullable标记。IntelliJ提供了@NotNull和@Nullable两种标记，加在类型前面，这样可以比较简洁可靠地防止null指针的出现。IntelliJ本身会对含有这种标记的代码进行静态分析，指出运行时可能出现NullPointerException的地方。在运行时，会在null指针不该出现的地方产生IllegalArgumentException，即使那个null指针你从来没有deference。这样你可以在尽量早期发现并且防止null指针的出现。</li>
<li>使用Optional类型。Java 8和Swift之类的语言，提供了一种叫Optional的类型。正确的使用这种类型，可以在很大程度上避免null的问题。null指针的问题之所以存在，是因为你可以在没有“检查”null的情况下，“访问”对象的成员。<br>Optional类型的设计原理，就是把“检查”和“访问”这两个操作合二为一，成为一个“原子操作”。这样你没法只访问，而不进行检查。这种做法其实是ML，Haskell等语言里的模式匹配（pattern matching）的一个特例。模式匹配使得类型判断和访问成员这两种操作合二为一，所以你没法犯错。<br>比如，在Swift里面，你可以这样写：<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">let found = find()</div><div class="line">if let content = found &#123;</div><div class="line">  print(&quot;found: &quot; + content)</div><div class="line">&#125;</div></pre></td></tr></table></figure>
</li>
</ul>
<p>你从find()函数得到一个Optional类型的值found。假设它的类型是String?，那个问号表示它可能包含一个String，也可能是nil。然后你就可以用一种特殊的if语句，同时进行null检查和访问其中的内容。这个if语句跟普通的if语句不一样，它的条件不是一个Bool，而是一个变量绑定let content = found。<br>我不是很喜欢这语法，不过这整个语句的含义是：如果found是nil，那么整个if语句被略过。如果它不是nil，那么变量content被绑定到found里面的值（unwrap操作），然后执行print(“found: “ + content)。由于这种写法把检查和访问合并在了一起，你没法只进行访问而不检查。<br>Java 8的做法比较蹩脚一些。如果你得到一个Optional类型的值found，你必须使用“函数式编程”的方式，来写这之后的代码：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">Optional&lt;String&gt; found = find();</div><div class="line">found.ifPresent(content -&gt; System.out.println(&quot;found: &quot; + content));</div></pre></td></tr></table></figure></p>
<p>这段Java代码跟上面的Swift代码等价，它包含一个“判断”和一个“取值”操作。ifPresent先判断found是否有值（相当于判断是不是null）。如果有，那么将其内容“绑定”到lambda表达式的content参数（unwrap操作），然后执行lambda里面的内容，否则如果found没有内容，那么ifPresent里面的lambda不执行。<br>Java的这种设计有个问题。判断null之后分支里的内容，全都得写在lambda里面。在函数式编程里，这个lambda叫做“<a href="https://en.wikipedia.org/wiki/Continuation" target="_blank" rel="external">continuation</a>”，Java把它叫做 “<a href="https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html" target="_blank" rel="external">Consumer</a>”，它表示“如果found不是null，拿到它的值，然后应该做什么”。由于lambda是个函数，你不能在里面写return语句返回出外层的函数。比如，如果你要改写下面这个函数（含有null）：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">public static String foo() &#123;</div><div class="line">  String found = find();</div><div class="line">  if (found != null) &#123;</div><div class="line">    return found;</div><div class="line">  &#125; else &#123;</div><div class="line">    return &quot;&quot;;</div><div class="line">  &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>就会比较麻烦。因为如果你写成这样：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">public static String foo() &#123;</div><div class="line">  Optional&lt;String&gt; found = find();</div><div class="line">  found.ifPresent(content -&gt; &#123;</div><div class="line">    return content;    // can&apos;t return from foo here</div><div class="line">  &#125;);</div><div class="line">  return &quot;&quot;;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>里面的return a，并不能从函数foo返回出去。它只会从lambda返回，而且由于那个lambda（<a href="https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html#accept-T-" target="_blank" rel="external">Consumer.accept</a>）的返回类型必须是void，编译器会报错，说你返回了String。由于Java里closure的自由变量是只读的，你没法对lambda外面的变量进行赋值，所以你也不能采用这种写法：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">public static String foo() &#123;</div><div class="line">  Optional&lt;String&gt; found = find();</div><div class="line">  String result = &quot;&quot;;</div><div class="line">  found.ifPresent(content -&gt; &#123;</div><div class="line">    result = content;    // can&apos;t assign to result</div><div class="line">  &#125;);</div><div class="line">  return result;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>所以，虽然你在lambda里面得到了found的内容，如何使用这个值，如何返回一个值，却让人摸不着头脑。你平时的那些Java编程手法，在这里几乎完全废掉了。实际上，判断null之后，你必须使用Java 8提供的一系列古怪的<a href="http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html" target="_blank" rel="external">函数式编程操作</a>：map, flatMap, orElse之类，想法把它们组合起来，才能表达出原来代码的意思。比如之前的代码，只能改写成这样：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">public static String foo() &#123;</div><div class="line">  Optional&lt;String&gt; found = find();</div><div class="line">  return found.orElse(&quot;&quot;);</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>这简单的情况还好。复杂一点的代码，我还真不知道怎么表达，我怀疑Java 8的Optional类型的方法，到底有没有提供足够的表达力。那里面少数几个东西表达能力不咋的，论工作原理，却可以扯到functor，continuation，甚至monad等高深的理论…… 仿佛用了Optional之后，这语言就不再是Java了一样。<br>所以Java虽然提供了Optional，但我觉得可用性其实比较低，难以被人接受。相比之下，Swift的设计更加简单直观，接近普通的过程式编程。你只需要记住一个特殊的语法if let content = found {…}，里面的代码写法，跟普通的过程式语言没有任何差别。<br>总之你只要记住，使用Optional类型，要点在于“原子操作”，使得null检查与取值合二为一。这要求你必须使用我刚才介绍的特殊写法。如果你违反了这一原则，把检查和取值分成两步做，还是有可能犯错误。比如在Java 8里面，你可以使用found.get()这样的方式直接访问found里面的内容。在Swift里你也可以使用found!来直接访问而不进行检查。<br>你可以写这样的Java代码来使用Optional类型：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">Option&lt;String&gt; found = find();</div><div class="line">if (found.isPresent()) &#123;</div><div class="line">  System.out.println(&quot;found: &quot; + found.get());</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>如果你使用这种方式，把检查和取值分成两步做，就可能会出现运行时错误。if (found.isPresent())本质上跟普通的null检查，其实没什么两样。如果你忘记判断found.isPresent()，直接进行found.get()，就会出现NoSuchElementException。这跟NullPointerException本质上是一回事。所以这种写法，比起普通的null的用法，其实换汤不换药。如果你要用Optional类型而得到它的益处，请务必遵循我之前介绍的“原子操作”写法。</p>
<h3 id="防止过度工程"><a href="#防止过度工程" class="headerlink" title="防止过度工程"></a>防止过度工程</h3><p>人的脑子真是奇妙的东西。虽然大家都知道过度工程（over-engineering）不好，在实际的工程中却经常不由自主的出现过度工程。我自己也犯过好多次这种错误，所以觉得有必要分析一下，过度工程出现的信号和兆头，这样可以在初期的时候就及时发现并且避免。</p>
<p>过度工程即将出现的一个重要信号，就是当你过度的思考“将来”，考虑一些还没有发生的事情，还没有出现的需求。比如，“如果我们将来有了上百万行代码，有了几千号人，这样的工具就支持不了了”，“将来我可能需要这个功能，所以我现在就把代码写来放在那里”，“将来很多人要扩充这片代码，所以现在我们就让它变得可重用”……</p>
<p>这就是为什么很多软件项目如此复杂。实际上没做多少事情，却为了所谓的“将来”，加入了很多不必要的复杂性。眼前的问题还没解决呢，就被“将来”给拖垮了。人们都不喜欢目光短浅的人，然而在现实的工程中，有时候你就是得看近一点，把手头的问题先搞定了，再谈以后扩展的问题。</p>
<p>另外一种过度工程的来源，是过度的关心“代码重用”。很多人“可用”的代码还没写出来呢，就在关心“重用”。为了让代码可以重用，最后被自己搞出来的各种框架捆住手脚，最后连可用的代码就没写好。如果可用的代码都写不好，又何谈重用呢？很多一开头就考虑太多重用的工程，到后来被人完全抛弃，没人用了，因为别人发现这些代码太难懂了，自己从头开始写一个，反而省好多事。</p>
<p>过度地关心“测试”，也会引起过度工程。有些人为了测试，把本来很简单的代码改成“方便测试”的形式，结果引入很多复杂性，以至于本来一下就能写对的代码，最后复杂不堪，出现很多bug。</p>
<p>世界上有两种“没有bug”的代码。一种是“没有明显的bug的代码”，另一种是“明显没有bug的代码”。第一种情况，由于代码复杂不堪，加上很多测试，各种coverage，貌似测试都通过了，所以就认为代码是正确的。第二种情况，由于代码简单直接，就算没写很多测试，你一眼看去就知道它不可能有bug。你喜欢哪一种“没有bug”的代码呢？</p>
<p>根据这些，我总结出来的防止过度工程的原则如下：</p>
<ol>
<li>先把眼前的问题解决掉，解决好，再考虑将来的扩展问题。 </li>
<li>先写出可用的代码，反复推敲，再考虑是否需要重用的问题。 </li>
<li>先写出可用，简单，明显没有bug的代码，再考虑测试的问题。</li>
</ol>
<blockquote>
<p>创造这样的精品文章需要很多的精力和咖啡 ;) 如果你喜欢这篇文章，请付款支持。建议金额$5美元或者30人民币。付款方式请参考<a href="http://www.yinwang.org/blog-cn/2016/04/13/pay-blog" target="_blank" rel="external">这里</a>。</p>
</blockquote>

      
    </div>
    
  </div>
  
    
    <div class="copyright">
        <p><span>本文标题:</span><a href="/2017/04/12/bian-cheng-de-zhi-hui/">编程的智慧</a></p>
        <p><span>文章作者:</span><a href="/" title="回到主页">孤星可</a></p>
        <p><span>发布时间:</span>2017-04-12, 12:08:44</p>
        <p><span>最后更新:</span>2017-04-18, 14:23:33</p>
        <p>
            <span>原始链接:</span><a class="post-url" href="/2017/04/12/bian-cheng-de-zhi-hui/" title="编程的智慧">http://blog.guxingke.com/2017/04/12/bian-cheng-de-zhi-hui/</a>
            <span class="copy-path" data-clipboard-text="原文: http://blog.guxingke.com/2017/04/12/bian-cheng-de-zhi-hui/　　作者: 孤星可" title="点击复制文章链接"><i class="fa fa-clipboard"></i></span>
            <script> var clipboard = new Clipboard('.copy-path'); </script>
        </p>
        <p>
            <span>许可协议:</span><i class="fa fa-creative-commons"></i> <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/" title="CC BY-NC-SA 4.0 International" target = "_blank">"署名-非商用-相同方式共享 4.0"</a> 转载请保留原文链接及作者。
        </p>
    </div>



    <nav id="article-nav">
        
            <div id="article-nav-newer" class="article-nav-title">
                <a href="/2017/04/16/terminal/">
                    利器说-Terminal
                </a>
            </div>
        
        
            <div id="article-nav-older" class="article-nav-title">
                <a href="/2017/03/29/phoenix/">
                    利器说-Phoenix
                </a>
            </div>
        
    </nav>

  
</article>

    <div id="toc" class="toc-article">
        <strong class="toc-title">文章目录</strong>
        
            <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#编程的智慧"><span class="toc-number">1.</span> <span class="toc-text">编程的智慧</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#反复推敲代码"><span class="toc-number">1.1.</span> <span class="toc-text">反复推敲代码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#写优雅的代码"><span class="toc-number">1.2.</span> <span class="toc-text">写优雅的代码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#写模块化的代码"><span class="toc-number">1.3.</span> <span class="toc-text">写模块化的代码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#写可读的代码"><span class="toc-number">1.4.</span> <span class="toc-text">写可读的代码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#写简单的代码"><span class="toc-number">1.5.</span> <span class="toc-text">写简单的代码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#写直观的代码"><span class="toc-number">1.6.</span> <span class="toc-text">写直观的代码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#写无懈可击的代码"><span class="toc-number">1.7.</span> <span class="toc-text">写无懈可击的代码</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#正确处理错误"><span class="toc-number">1.8.</span> <span class="toc-text">正确处理错误</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#正确处理null指针"><span class="toc-number">1.9.</span> <span class="toc-text">正确处理null指针</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#防止过度工程"><span class="toc-number">1.10.</span> <span class="toc-text">防止过度工程</span></a></li></ol></li></ol>
        
    </div>
    <style>
        .left-col .switch-btn,
        .left-col .switch-area {
            display: none;
        }
        .toc-level-3 i,
        .toc-level-3 ol {
            display: none !important;
        }
    </style>

    <input type="button" id="tocButton" value="隐藏目录"  title="点击按钮隐藏或者显示文章目录">

    <script>
        yiliaConfig.toc = ["隐藏目录", "显示目录", !!"false"];
    </script>





    
        <section id="comments">
    <style> aside.comment-bar { margin: auto 30px; }</style>
    <div id="disqus_thread"></div>
    <script>
        var disqus_config = function(){
            this.page.url = 'http://blog.guxingke.com/2017/04/12/bian-cheng-de-zhi-hui/';
            this.page.identifier = '2017/04/12/bian-cheng-de-zhi-hui/';
        };
        var loadComment = function(){
            var d = document, s = d.createElement('script');
            s.src = '//guxingke.disqus.com/embed.js';
            s.setAttribute('data-timestamp', +new Date());
            (d.head || d.body).appendChild(s);
        }
    </script>
    
    <script> loadComment(); </script>

</section>


    




    <div class="scroll" id="post-nav-button">
        
            <a href="/2017/04/16/terminal/" title="上一篇: 利器说-Terminal">
                <i class="fa fa-angle-left"></i>
            </a>
        

        <a title="文章列表"><i class="fa fa-bars"></i><i class="fa fa-times"></i></a>

        
            <a href="/2017/03/29/phoenix/" title="下一篇: 利器说-Phoenix">
                <i class="fa fa-angle-right"></i>
            </a>
        
    </div>

    <ul class="post-list"><li class="post-list-item"><a class="post-list-link" href="/2017/07/27/how-to-migration-old-biz-prj-to-springboot/">老业务项目迁移至 SpringBoot, 踩坑记</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/07/27/rewirte-macos/">重装 macos</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/07/24/mal-step3-env/">Make A Lisp[3]-环境</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/07/21/mal-step2-eval/">Make A Lisp[2]-求值</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/07/20/mal-step1-read-print/">Make A Lisp[1]-读取&输出</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/07/20/mal-step0_repl/">Make A Lisp[0]-REPL</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/07/18/mal-setup/">Make A Lisp[setup]-起步</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/21/cron-note/">cron 备忘</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/14/something-about-team-cooperation-and-task-manager/">关于任务协作和管理工具</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/06/10/something-about-java-web-framework/">对 web framework 的一点看法.</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/05/26/ihost/">利器说-IHost</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/05/17/mybatis-pre/">mybatis-解析-序章</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/05/02/paw/">利器说-Paw</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/04/26/alfred/">利器说-Alfred</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/04/26/charles/">利器说-Charles</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/04/24/blog-site-with-https/">blog 站点的 https 之旅</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/04/23/caddy/">利器说-Caddy</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/04/22/useful-java-lib/">一些有用的 Java 库</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/04/16/private-cfg-backup-and-sync/">利器说-配置文件备份与同步</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/04/16/terminal/">利器说-Terminal</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/04/12/bian-cheng-de-zhi-hui/">编程的智慧</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/03/29/phoenix/">利器说-Phoenix</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/03/22/tool-pre/">利器说-序</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/02/04/collection/">收藏</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/02/04/blog-building/">个人博客站点建设历程</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/02/04/cracked-tool-or-method/">破解软件</a></li><li class="post-list-item"><a class="post-list-link" href="/2017/02/01/hello-world/">Hello World</a></li></ul>




    <script>
        
    </script>
</div>
      <footer id="footer">
    <div class="outer">
        <div id="footer-info">
            <div class="footer-left">
                <i class="fa fa-copyright"></i> 
                2017 孤星可
            </div>
            <div class="footer-right">
                <a href="http://hexo.io/" target="_blank" title="快速、简洁且高效的博客框架">Hexo</a>  Theme <a href="https://github.com/MOxFIVE/hexo-theme-yelee" target="_blank" title="简而不减 Hexo 双栏博客主题  v3.5">Yelee</a> by MOxFIVE <i class="fa fa-heart animated infinite pulse"></i>
            </div>
        </div>
        
            <div class="visit">
                
                    <span id="busuanzi_container_site_pv" style='display:none'>
                        <span id="site-visit" title="本站到访数"><i class="fa fa-user" aria-hidden="true"></i><span id="busuanzi_value_site_uv"></span>
                        </span>
                    </span>
                
                
                    <span>| </span>
                
                
                    <span id="busuanzi_container_page_pv" style='display:none'>
                        <span id="page-visit"  title="本页阅读量"><i class="fa fa-eye animated infinite pulse" aria-hidden="true"></i><span id="busuanzi_value_page_pv"></span>
                        </span>
                    </span>
                
            </div>
        
    </div>
</footer>
    </div>
    
<script data-main="/js/main.js" src="//cdn.bootcss.com/require.js/2.2.0/require.min.js"></script>

    <script>
        $(document).ready(function() {
            var iPad = window.navigator.userAgent.indexOf('iPad');
            if (iPad > -1 || $(".left-col").css("display") === "none") {
                var bgColorList = ["#9db3f4", "#414141", "#e5a859", "#f5dfc6", "#c084a0", "#847e72", "#cd8390", "#996731"];
                var bgColor = Math.ceil(Math.random() * (bgColorList.length - 1));
                $("body").css({"background-color": bgColorList[bgColor], "background-size": "cover"});
            }
            else {
                var backgroundnum = 5;
                var backgroundimg = "url(/background/bg-x.jpg)".replace(/x/gi, Math.ceil(Math.random() * backgroundnum));
                $("body").css({"background": backgroundimg, "background-attachment": "fixed", "background-size": "cover"});
            }
        })
    </script>





<div class="scroll" id="scroll">
    <a href="#" title="返回顶部"><i class="fa fa-arrow-up"></i></a>
    <a href="#comments" onclick="load$hide();" title="查看评论"><i class="fa fa-comments-o"></i></a>
    <a href="#footer" title="转到底部"><i class="fa fa-arrow-down"></i></a>
</div>
<script>
    // Open in New Window
    
        var oOpenInNew = {
            
            
            
            
            
            
             archives: ".archive-article-title", 
             miniArchives: "a.post-list-link", 
            
             friends: "#js-friends a", 
             socail: ".social a" 
        }
        for (var x in oOpenInNew) {
            $(oOpenInNew[x]).attr("target", "_blank");
        }
    
</script>

<script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js">
</script>
  </div>
</body>
</html>